//
// Classes for automatically closing handles.
#ifndef LONGKEY_COMMON_SMART_HANDLE_H_
#define LONGKEY_COMMON_SMART_HANDLE_H_

#include <WinCrypt.h>

/**
* Base traits class for handles.
* This base class provides default implementation for InvalidValue and IsValid
* @param T  The handle type to be wrapped.
*/
template<class T>
class BaseHandleTraitsT
{
public:
	// Typedef that is used by this class and derived classes
	typedef T HandleType;

	// Returns the invalid handle value
	static HandleType InvalidValue()
	{
		return NULL;
	}

	// Returns true only if the given handle h is invalid
	static bool IsValid(const HandleType& h)
	{
		return h != InvalidValue();
	}
private:
	DISALLOW_EVIL_CONSTRUCTORS(BaseHandleTraitsT);
};

/**
* Smart handle class.
* Offers basic HANDLE functionality such as cast, attach/detach and automatic Close().
*/
template<class T, class Traits, class AlternateType = T>
class HandleT
{
public:
	// Default constructor.
	HandleT() : h_(Traits::InvalidValue())
	{
	}

	// constructor that assumes ownership of the supplied handle
	explicit HandleT(T h) : h_(h)
	{
	}

	// Destructor class @ref Close()
	~HandleT()
	{
		Close();
	}

	// Assumes ownership of the supplied handle,
	// potentially closing an already held handle.
	void Attach(T h)
	{
		Close();
		h_ = h;
	}

	// Transfers ownership to the caller and set the internal
	// state to InvalidValue().
	T Detach()
	{
		T h = h_;
		h_ = Traits::InvalidValue();
		return h;
	}

	// Handle accessor
	T handle()
	{
		return h_;
	}

	// An alternate cast for the handle.
	// This can be useful for GDI objects that are used
	// in functions that e.g. accept both HGDIOBJ and HBITMAP.
	AlternateType alt_type()
	{
		return reinterpret_cast<AlternateType>(h_);
	}

	// Access the contained handle
	operator T() { return h_; }

	T& receive()
	{
		ASSERT(!IsValid(), (L"Should only be used for out arguments"));
		return h_;
	}

	// @returns true only if the handle is valid as depicted
	//  by the traits class.
	bool IsValid() 
	{
		return Traits::IsValid(h_);
	}

	// Closes the handle
	void Close()
	{
		if (Traits::IsValid(h_))
		{
			Traits::Close(h_);
			h_ = Traits::InvalidValue();
		}
	}

protected:
	T h_;

private:
	DISALLOW_EVIL_CONSTRUCTORS(HandleT);
};

/*
 * Traits class for a regular Win32 HANDLE.
 */
class HandleTraitsWin32Handle : public BaseHandleTraitsT<HANDLE>
{
public:
	static bool Close(HandleType h)
	{
		return (::CloseHandle(h) != false);
	}

	static HandleType InvalidValue()
	{
		return NULL;		// note that INVALID_HANDLE_VALUE is also an invalid handle
	}

	// Returns true only if the given handle h is invalid
	static bool IsValid(const HandleType& h)
	{
		return h != InvalidValue() && h != INVALID_HANDLE_VALUE;
	}
private:
	DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsWin32Handle);
};

/*
* Traits class for FindXXXFile handles.
*/
class HandleTraitsFindHandle : public BaseHandleTraitsT<HANDLE> {
 public:
  // Calls FindClose to close the handle.
  static bool Close(HandleType h) {
    return (::FindClose(h) != false);
  }

  // Returns the invalid handle value
  static HandleType InvalidValue() {
    return INVALID_HANDLE_VALUE;
  }

 private:
  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsFindHandle);
};

/*
* Traits for an HMENU.
*/
class HandleTraitsHMenu : public BaseHandleTraitsT<HMENU> {
 public:
  // Calls DestroyMenu to destroy the menu.
  static bool Close(HandleType h) {
    return (::DestroyMenu(h) != FALSE);
  }

 private:
  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHMenu);
};

/*
* Traits for an HCRYPTKEY.
*/
class HandleTraitsHCryptKey : public BaseHandleTraitsT<HCRYPTKEY> {
 public:
  static bool Close(HandleType h) {
    return (::CryptDestroyKey(h) != FALSE);
  }

 private:
  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHCryptKey);
};

/*
* Traits for an HCRYPTHASH.
*/
class HandleTraitsHCryptHash : public BaseHandleTraitsT<HCRYPTHASH> {
 public:
  static bool Close(HandleType h) {
    return (::CryptDestroyHash(h) != FALSE);
  }

 private:
  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsHCryptHash);
};

/*
 * Traits for LoadLibrary/FreeLibrary.
 */
class HandleTraitsLibrary : public BaseHandleTraitsT<HMODULE> {
 public:
  static bool Close(HandleType h) {
    return (::FreeLibrary(h) != FALSE);
  }

 private:
  DISALLOW_EVIL_CONSTRUCTORS(HandleTraitsLibrary);
};


/*
* Win32 handle types.  Add new ones here as you need them.
* Note that GDI handle types should be kept in common/gdi_smart_ptr.h
* rather than here.
*/
typedef HandleT<HANDLE, HandleTraitsWin32Handle> AutoHandle;
typedef HandleT<HANDLE, HandleTraitsFindHandle> AutoFindHandle;
typedef HandleT<HMENU, HandleTraitsHMenu> AutoHMenu;
typedef HandleT<HCRYPTHASH, HandleTraitsHCryptHash> AutoHCryptHash;
typedef HandleT<HCRYPTKEY, HandleTraitsHCryptKey> AutoHCryptKey;
typedef HandleT<HINSTANCE, HandleTraitsLibrary> AutoLibrary;


#endif	// LONGKEY_COMMON_SMART_HANDLE_H_